package com.zhangsr.zus.config.spring.schema;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.w3c.dom.Element;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Date;

/**
 * @Author: zhangsr
 */
public class ZusBeanDefinitionParser implements BeanDefinitionParser {

    private static final Logger logger = Logger.getLogger(ZusBeanDefinitionParser.class);

    private final Class<?> beanClass;
    private final boolean required;

    public ZusBeanDefinitionParser(Class<?> beanClass, boolean required) {
        this.beanClass = beanClass;
        this.required = required;
    }

    public BeanDefinition parse(Element element, ParserContext parserContext) {
        return parse(element, parserContext, this.beanClass, this.required);
    }

    private static BeanDefinition parse(Element element, ParserContext parserContext, Class<?> beanClass, boolean required) {
        RootBeanDefinition beanDefinition = new RootBeanDefinition();
        beanDefinition.setBeanClass(beanClass);
        beanDefinition.setLazyInit(false);
        String id = element.getAttribute("id");

        if (id != null && id.length() > 0) {
            if (parserContext.getRegistry().containsBeanDefinition(id))
                throw new IllegalStateException("Duplicate spring bean id " + id);

            parserContext.getRegistry().registerBeanDefinition(id, beanDefinition);
        }

        for (Method setter : beanClass.getMethods()) {
            String name = setter.getName();
            if (name.length() > 3 && name.startsWith("set")
                    && Modifier.isPublic(setter.getModifiers())
                    && setter.getParameterTypes().length == 1) {

                Class<?> type = setter.getParameterTypes()[0];
                String property = name.substring(3, 4).toLowerCase() + name.substring(4);
                String value = element.getAttribute(property);
                Object reference;

                // filter setBeanName, setApplicationContext.
                Method getter = null;
                try {
                    getter = beanClass.getMethod("get" + name.substring(3), new Class<?>[0]);
                } catch (NoSuchMethodException e) {
                    try {
                        getter = beanClass.getMethod("is" + name.substring(3), new Class<?>[0]);
                    } catch (NoSuchMethodException e2) {
                    }
                }
                if (getter == null
                        || !Modifier.isPublic(getter.getModifiers())
                        || !type.equals(getter.getReturnType())) {
                    continue;
                }

                // call set<T>.
                if (value != null && value.trim().length() > 0) {
//                    logger.debug("addProperty:" + property + ",value：" + value);
                    if ("protocol".equals(property)) {
                        parseMultiRef("protocols", value, beanDefinition);
                    } else {
                        if (isPrimitive(type)) {
                            reference = value;
                        } else {
                            reference = new RuntimeBeanReference(value);
                        }
                        beanDefinition.getPropertyValues().addPropertyValue(property, reference);
                    }
                }
            }
        }
        return beanDefinition;
    }

    private static void parseMultiRef(String property, String value, RootBeanDefinition beanDefinition) {
        String[] values = value.split("\\s*[,]+\\s*");
        ManagedList list = null;
        for (int i = 0; i < values.length; i++) {
            String v = values[i];
            if (v != null && v.length() > 0) {
                if (list == null) {
                    list = new ManagedList();
                }
                list.add(new RuntimeBeanReference(v));
            }
        }
        beanDefinition.getPropertyValues().addPropertyValue(property, list);
    }

    private static boolean isPrimitive(Class<?> cls) {
        return cls.isPrimitive() || cls == Boolean.class || cls == Byte.class
                || cls == Character.class || cls == Short.class || cls == Integer.class
                || cls == Long.class || cls == Float.class || cls == Double.class
                || cls == String.class || cls == Date.class || cls == Class.class;
    }

}
